<template>
  <view class="t-wrap">
    <!-- 虚拟view用于计算，计算完成则消失 -->
    <view class="t-txt-hide" :id="hid" v-if="!isCompute" :style="[computeStyle(0),{'text-align':oneRowTextAlign}]">
      <text space="nbsp">{{ testContent ? testContent : content }}{{ showSymbol ? '...' : '' }}</text>
      <text
          v-if="expandText && collapseText && showSymbol" class="t-button">
        {{ expandText }}
      </text>
      <text></text>
    </view>
    <!-- 真实显示的内容 -->
    <view class="t-ellipsis" :id="id"
          :style="[!isCompute?computeStyle(1):computeStyle(2),{'text-align':oneRowTextAlign}]" @click="contentClick">
      <text space="nbsp">{{ (!isCompute || expand) ? content : (actualContent + (showSymbol ? '...' : '')) }}</text>
      <text
          v-if="expandText && collapseText && showSymbol" class="t-button" @click.stop="changeCollapse"
          :style="{'color':actionFontColor}">{{ !expand ? expandText : collapseText }}
      </text>
      <text></text>
    </view>
    <!-- 这里加入了自定义的动态骨架屏友好反馈 -->
    <view v-if="!isCompute && rows>0" class="t-skeleton">
      <view class="skeletons" v-for="(item,index) in rows" :key="index">
        <view class="empty"></view>
      </view>
    </view>
  </view>
</template>

<script>
//为了兼容部分老机型，增加扰动因子参数
const factor = 5;
export default {
  name: "KevyEllipsis",
  props: {
    /**
     * 文本唯一标识符，非必填
     */
    textId: {
      type: [String, Number],
      default: ''
    },
    /**
     * 文本内容，默认''
     */
    content: {
      type: String,
      default: ''
    },
    /**
     * 字体大小，单位rpx，默认28
     */
    fontSize: {
      type: [String, Number],
      default: 28
    },
    /**
     * 字体颜色，默认#666666
     */
    fontColor: {
      color: String,
      default: '#666666'
    },
    /**
     * 收起操作的文案，默认''
     */
    collapseText: {
      type: String,
      default: ''
    },
    /**
     * 展开操作的文案，默认''
     */
    expandText: {
      type: String,
      default: ''
    },
    /**
     * 收起、展开操作文字颜色，默认'#007aff'
     */
    actionFontColor: {
      color: String,
      default: '#007aff'
    },
    /**
     * 展示行数，默认1
     */
    rows: {
      type: Number,
      default: 1
    },
    /**
     * 只有一行时文本对齐方式，支持left、right、justify
     */
    oneRowTextAlign: {
      type: String,
      default: "justify"
    },
  },
  data() {
    return {
      //是否展开
      expand: false,
      //是否已计算
      isCompute: false,
      //内容高度
      h: undefined,
      //内容宽度
      w: undefined,
      //实际显示内容
      actualContent: '',
      //高度探测内容
      testContent: undefined,
      //是否显示省略号
      showSymbol: false,
      //hid和id,唯一标识符
      hid: 'hid' + Math.random().toString(36).substr(2),
      id: 'id' + Math.random().toString(36).substr(2),
    };
  },
  mounted() {
    this.$nextTick(() => {
      this.initEllipsis();
    })
  },
  computed: {
    //动态计算组件样式
    computeStyle() {
      return b => {
        let lines = this.rows > 0 ? this.rows : 1;
        let obj = {};
        if (b == 1) {
          obj = {
            '-webkit-line-clamp': lines,
            'display': '-webkit-box',
            'text-overflow': 'ellipsis',
            'overflow': 'hidden',
            '-webkit-box-orient': 'vertical'
          };
        } else if (b == 2) {
          obj = {
            'position': 'relative',
            'left': '0rpx',
            ...obj
          };
        }
        return {
          'font-size': this.fontSize + 'rpx',
          'color': this.fontColor,
          ...obj
        }
      }
    }
  },
  watch: {
    content(newVal, oldVal) {
      this.expand = false;
      this.isCompute = false;
      this.h = undefined;
      this.w = undefined;
      this.actualContent = '';
      this.showSymbol = false;
      this.initEllipsis();
    }
  },
  methods: {
    //初始化
    initEllipsis() {
      if (this.content?.length > 0) {
        this.$nextTick(() => {
          this.init(this, () => {
            this.compute(this);
          })
        })

      }
    },
    //收起展开状态切换
    changeCollapse() {
      this.expand = !this.expand;
    },
    //文本点击事件
    contentClick() {
      this.$emit('contentClick', this.textId);
    },
    //组件参数初始化
    init($this, callback, isali) {
      if (isali) {
        uni.createSelectorQuery().in().select('#' + $this.id).boundingClientRect(d => {
          $this.h = Number(d.height.toFixed(1));
          $this.w = Number(d.width.toFixed(1));
          if (callback) {
            callback()
          }
        }).exec();
      } else {
        uni.createSelectorQuery().in($this).select('#' + $this.id).boundingClientRect(d => {
          $this.h = Number(d.height.toFixed(1));
          $this.w = Number(d.width.toFixed(1));
          if (callback) {
            callback()
          }
        }).exec();
      }
    },
    //动态计算组件内容
    computeContent($this, isali, dr) {
      $this.$nextTick(() => {
        $this.getH($this, isali, (ch) => {
          if (ch - factor > $this.h) {
            if (dr === -1) {
              $this.testContent = $this.content.substring(0, $this.testContent.length -
                  1);
              $this.computeContent($this, isali, dr);
            } else {
              $this.actualContent = $this.content.substring(0, $this.testContent.length -
                  1);
              $this.isCompute = true;
            }
          } else {
            if (dr === -1) {
              $this.actualContent = $this.testContent;
              $this.isCompute = true;
            } else {
              $this.testContent = $this.content.substring(0, $this.testContent.length +
                  1);
              $this.computeContent($this, isali, dr);
            }
          }
        })
      });
    },
    //计算工具方法
    compute($this, isali) {
      let {
        rows,
        fontSize,
        content,
        h,
        w
      } = $this;
      $this.testContent = content;
      $this.$nextTick(() => {
        $this.getH($this, isali, (ch) => {
          if (ch - factor > h) {
            let lh = h / rows;
            let fn = Math.floor(w / $this.rpx2px(fontSize));
            let sfn = fn * rows;
            let i = $this.fontNum(content, sfn * 2 - ($this.expandText ? $this.fontNum(
                    $this.expandText) :
                0) - 3);
            $this.showSymbol = true;
            $this.testContent = content.substring(0, i);
            $this.$nextTick(() => {
              $this.getH($this, isali, (ch1) => {
                if (ch1 - factor > h) {
                  $this.testContent = content.substring(0, $this
                      .testContent.length - 1);
                  $this.computeContent($this, isali, -1);
                } else {
                  $this.testContent = content.substring(0, $this
                      .testContent.length + 1);
                  $this.computeContent($this, isali, 1);
                }
              });
            });
          } else {
            $this.isCompute = true;
            $this.actualContent = content;
          }
        })
      });
    },
    //动态计算字符数
    fontNum(val, limit) {
      let c = 0;
      for (let i = 0; i < val.length; i++) {
        let a = val.charAt(i);
        if (a.match(/[^\x00-\xff]/ig) != null) {
          if (limit) {
            if (c + 2 > limit) {
              return i;
            } else {
              c += 2;
            }
          } else {
            c += 2;
          }

        } else {
          if (limit) {
            if (c + 1 > limit) {
              return i;
            } else {
              c += 1;
            }
          } else {
            c += 1;
          }
        }
      }
      if (!limit) {
        return c;
      }
    },
    rpx2px(rpx) {
      return uni.getSystemInfoSync().windowWidth * Number(rpx) / 750;
    },
    getH($, isali, callback) {
      if (isali) {
        uni.createSelectorQuery().in().select('#' + $.hid).fields({
          size: true
        }, d => {
          if (d && d.height) {
            callback(Number(d.height.toFixed(1)));
          }
        }).exec();
      } else {
        uni.createSelectorQuery().in($).select('#' + $.hid).fields({
          size: true
        }, d => {
          if (d && d.height) {
            callback(Number(d.height.toFixed(1)));
          }
        }).exec();
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.t-wrap {
  width: 100%;
  box-sizing: border-box;
  position: relative;
}

.t-txt-hide {
  box-sizing: border-box;
  word-break: break-all;
  position: absolute;
  top: 999999px;
  left: 999999px;
  z-index: -1000;
  top: 0rpx;
  width: 100%;
  margin: 0rpx;
  text-align: justify;
  white-space: pre-line;
  line-height: 1.5 !important;

  .t-button {
    float: right;
    clear: both;
  }
}

.t-ellipsis {
  text-align: justify;
  box-sizing: border-box;
  width: 100%;
  word-break: break-all;
  position: relative;
  left: 99999px;
  white-space: pre-line;
  line-height: 1.5 !important;

  .t-button {
    float: right;
    clear: both;
    font-weight: 500;
  }
}

.t-skeleton {
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  position: absolute;
  top: 0rpx;
  left: 0rpx;
}

.skeletons:first-child {
  margin-top: 0rpx !important;
}

.skeletons {
  position: relative;
  display: block;
  overflow: hidden;
  width: 100%;
  height: 32rpx;
  margin-top: 12rpx;
  background-color: rgba(0, 0, 0, 0.06);
  box-sizing: border-box;
}

.skeletons .empty {
  display: block;
  position: absolute;
  width: 100%;
  height: 100%;
  -webkit-transform: translateX(-100%);
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(216, 216, 216, 0.753), transparent);
  -webkit-animation: loading .8s infinite;
  animation: loading .8s infinite;
}


@keyframes loading {
  100% {
    -webkit-transform: translateX(100%);
    transform: translateX(100%);
  }
}
</style>